#include "Plugin.h"
#include <fstream>
#include <d3dx9.h>
#include <atlbase.h>


k_Plugin::k_Plugin(IServiceLocator& ar_ServiceLocator)
	: k_Direct3D9BasePlugin(ar_ServiceLocator, L"SSAO", L"Blurs scene elements who are out of focus.")
	, mr_ShaderSystem_(NULL)
	, mr_ShowTextureShader_(NULL)
	, mr_DefaultStateBlock_(NULL)
	, mr_ScreenMask_(NULL)
	, mk_ZScreenMaskBatch_(NULL)
	, mr_ClearMask_(NULL)
	, mb_NeedDepthUpdate(false)
	, mb_NeedToReset(false)
{
	IEnvironment& lr_Environment = object_cast<IEnvironment>(ar_ServiceLocator);
	ms_LogFile = lr_Environment.get_ModulePath()+L"Walhall.log";

	::D3DXMatrixLookAtLH(
		&mr_ViewMatrix,
		&D3DXVECTOR3(0.0f, 0.0f, 0.0f),
		&D3DXVECTOR3(0.0f, 0.0f, 1.0f),
		&D3DXVECTOR3(0.0f, 1.0f, 0.0f)
		);
	::D3DXMatrixPerspectiveFovLH(
		&mr_ProjMatrix,
		D3DXToRadian(90),
		1.0,
		1.0f,
		2.0f
		);

//	mk_Parameters.push_back(new k_SliderParameter(*this, r_SSAO::SizeFactor, L"Size factor", L"A.", 10, 50, 25));
//	mk_Parameters.push_back(new k_SliderParameter(*this, r_SSAO::BlurSteps, L"Blur passes", L"B", 0, 20, 5));
//	mk_Parameters.push_back(new k_SliderParameter(*this, r_SSAO::DofStrength, L"Depth of field strength", L"This value specifics the transparency of the blured scene.", 0, 100, 90));
	
	//mk_Parameters.push_back(new k_SliderParameter(*this, r_SSAO::AOBlurSteps, L"AOBlur passes", L"The steps the ambient occlusion result is blured.", 0, 20, 0));
	//mk_Parameters.push_back(new k_SliderParameter(*this, r_SSAO::ZScreenMaskCount, L"Z screenmask count", L"The density of the ZBuffer tests.", 2, 100, 10));
	mk_Parameters.push_back(new k_CheckBoxParameter(*this, r_SSAO::ShowAOOnly, L"ShowAOOnly", L"Show AO only", L"Renders the ambient occlusion value.", true));
}


k_Plugin::~k_Plugin()
{
}


void k_Plugin::Initialize(IDirect3DDevice9* ar_Device_)
{
	PrintLog(ms_LogFile, L"SSAO: Initialising...\n");	

	ar_Device_->GetDeviceCaps(&mr_Caps);

	::ZeroMemory((void*)&mr_Viewport, sizeof(mr_Viewport));

	mf_DepthStep = 0.08f;
	mf_Depth = 1.0f;
	mb_ShowAOOnly = false;
	mi_AOBlurSteps = 0;

	mi_ZScreenMaskCount = 255;

	PrintLog(ms_LogFile, L"SSAO: Initialising finished.\n");

	k_Direct3D9BasePlugin::Initialize(ar_Device_);
}


void k_Plugin::Deinitialize()
{
	PrintLog(ms_LogFile, L"SSAO: Deinitialising...\n");
	this->set_Active(false);
	PrintLog(ms_LogFile, L"SSAO: Deinitialising finished.\n");

	k_Direct3D9BasePlugin::Deinitialize();
}


void k_Plugin::Render()
{
	if (!this->CanRender())
		return;

	this->UpdateParameters();

	// set basic states
	{
		mr_CurrentStateBlock_->Capture();
		mr_DefaultStateBlock_->Apply();
	}

	// our render stuff
	{
		// reset our textures in case something on our backbuffer has changed
		this->RebuildTextures();
		// update our texture
		this->UpdateTextures();
		// render our stuff
		this->UpdateRendering();
	}

	// set states back to normal
	{
		mr_CurrentStateBlock_->Apply();
	}

	mdw_LastTime = ::GetTickCount();
}


void k_Plugin::OnBeforeReset()
{
	mb_NeedToReset = this->get_Active();
	this->set_Active(false);
}


void k_Plugin::OnAfterReset()
{
	if (mb_NeedToReset)
	{
		this->set_Active(true);
		mb_NeedToReset = false;
	}
}


void k_Plugin::set_Active(bool ab_State)
{
	if (ab_State && !this->get_Active())
	{
		PrintLog(ms_LogFile, L"SSAO: Activating...\n");

		mb_NeedDepthUpdate = true;
		this->UpdateParameters();

		PrintLog(ms_LogFile, L"SSAO: Initialising shader subsystem.\n");
		mr_ShaderSystem_ = new ShaderSystem(mr_Device_);
		mr_ShaderSystem_->AddSemanticHandler(new HelTextureSemanticHandler("BackBuffer", &mr_BackBufferTexture));
		mr_ShaderSystem_->AddSemanticHandler(new HelTextureSemanticHandler("BlurBackBuffer", &mr_AOBackBufferTexture));
		mr_ShaderSystem_->AddSemanticHandler(new HelTextureSemanticHandler("NoiseTexture", &mr_NoiseTexture));
		mr_ShaderSystem_->AddSemanticHandler(new HelTextureSemanticHandler("ZBackBuffer", &mr_ZBackBufferTexture));

		PrintLog(ms_LogFile, L"SSAO: Loading shaders...\n");
		mr_ShowTextureShader_ = this->LoadShader("ShowTexture.hlsl", "Plugins\\SSAO");
		mr_AOBlurShader_ =		this->LoadShader("ZBlur.hlsl", "Plugins\\SSAO");
		mr_DepthRenderShader_ = this->LoadShader("RenderDepth.hlsl", "Plugins\\SSAO");
		mr_BlackRenderShader_ = this->LoadShader("RenderBlack.hlsl", "Plugins\\SSAO");
		mr_SSAOShader_ =		this->LoadShader("SSAO.hlsl", "Plugins\\SSAO");

		mr_NoiseTexture = this->LoadTexture(L"Noise.bmp", L"Plugins\\SSAO");

		PrintLog(ms_LogFile, L"SSAO: Gather basic states...\n");
		mr_Device_->CreateStateBlock( D3DSBT_ALL, &mr_DefaultStateBlock_ );
		mr_Device_->CreateStateBlock( D3DSBT_ALL, &mr_CurrentStateBlock_ );
		
		mdw_LastTime = ::GetTickCount();

		PrintLog(ms_LogFile, L"SSAO: Activating finished.\n");
	}
	else if (!ab_State && this->get_Active())
	{
		PrintLog(ms_LogFile, L"SSAO: Deactivating...\n");

		PrintLog(ms_LogFile, L"SSAO: Deinitialising mr_ShaderSystem_...\n");
		SafeDelete (mr_ShaderSystem_);
		
		PrintLog(ms_LogFile, L"SSAO: Deinitialising mr_ScreenMask_...\n");
		SafeDelete (mr_ScreenMask_);

		PrintLog(ms_LogFile, L"SSAO: Deinitialising mr_ZScreenMask_...\n");
		for(unsigned int i = 0; i < mk_ZScreenMaskList.size(); ++i)
		{
			SafeDelete (mk_ZScreenMaskList[i]);
		}
		mk_ZScreenMaskList.clear();
		SafeDelete(mk_ZScreenMaskBatch_);

		PrintLog(ms_LogFile, L"SSAO: Deinitialising mr_ClearMask_...\n");
		SafeDelete (mr_ClearMask_);

		PrintLog(ms_LogFile, L"SSAO: Deinitialising mr_NoiseTexture...\n");
		if (mr_NoiseTexture.mr_Texture_)
			mr_NoiseTexture.mr_Texture_->Release();
		mr_NoiseTexture.mr_Texture_ = NULL;
		mr_NoiseTexture = r_Texture();
		
		PrintLog(ms_LogFile, L"SSAO: Deinitialising mr_BackBufferTexture...\n");
		if (mr_BackBufferTexture.mr_Texture_)
			mr_BackBufferTexture.mr_Texture_->Release();
		mr_BackBufferTexture.mr_Texture_ = NULL;
		mr_BackBufferTexture = r_Texture();

		PrintLog(ms_LogFile, L"SSAO: Deinitialising mr_AOBackBufferTexture...\n");
		if (mr_AOBackBufferTexture.mr_Texture_)
			mr_AOBackBufferTexture.mr_Texture_->Release();
		mr_AOBackBufferTexture.mr_Texture_ = NULL;
		mr_AOBackBufferTexture = r_Texture();

		PrintLog(ms_LogFile, L"SSAO: Deinitialising mr_ZBackBufferTexture...\n");
		if (mr_ZBackBufferTexture.mr_Texture_)
			mr_ZBackBufferTexture.mr_Texture_->Release();
		mr_ZBackBufferTexture.mr_Texture_ = NULL;
		mr_ZBackBufferTexture = r_Texture();

		PrintLog(ms_LogFile, L"SSAO: Deinitialising mr_DefaultStateBlock_...\n");
		if (mr_DefaultStateBlock_)
			mr_DefaultStateBlock_->Release();
		mr_DefaultStateBlock_ = NULL;	

		PrintLog(ms_LogFile, L"SSAO: Deinitialising mr_CurrentStateBlock_...\n");
		if (mr_CurrentStateBlock_)
			mr_CurrentStateBlock_->Release();
		mr_CurrentStateBlock_ = NULL;	

		PrintLog(ms_LogFile, L"SSAO: Deactivating finished.\n");
	}
	k_Direct3D9BasePlugin::set_Active(ab_State);
}


bool k_Plugin::UpdateParameters()
{
	//mi_AOBlurSteps = object_cast<ISliderParameter>(this->GetParameter(r_SSAO::AOBlurSteps)).get_CurrentValue();
	mb_ShowAOOnly = object_cast<ICheckBoxParameter>(this->GetParameter(r_SSAO::ShowAOOnly)).get_CurrentValue();

	if (false)//mf_SizeFactor != lf_SizeFactor || mi_ZScreenMaskCount != li_ZScreenMaskCount)
	{
		mb_NeedDepthUpdate = true;

		if (!this->IsInitialized())
			return false;

		if (mr_BackBufferTexture.mr_Texture_)
			mr_BackBufferTexture.mr_Texture_->Release();
		mr_BackBufferTexture.mr_Texture_ = NULL;
		mr_BackBufferTexture = r_Texture();

		if (mr_AOBackBufferTexture.mr_Texture_)
			mr_AOBackBufferTexture.mr_Texture_->Release();
		mr_AOBackBufferTexture.mr_Texture_ = NULL;
		mr_AOBackBufferTexture = r_Texture();

		this->RebuildTextures();
	}
	return false;
}


LPDIRECT3DTEXTURE9 k_Plugin::CreateTexture(int ai_Width, int ai_Height, D3DFORMAT ae_Format)
{
	PrintLog(ms_LogFile, L"SSAO: CreateTexture: ");
	switch(ae_Format)
	{
		case D3DFMT_R8G8B8: PrintLog(ms_LogFile, L"D3DFMT_R8G8B8"); break;
		case D3DFMT_A8R8G8B8: PrintLog(ms_LogFile, L"D3DFMT_A8R8G8B8"); break;
		case D3DFMT_X8R8G8B8: PrintLog(ms_LogFile, L"D3DFMT_X8R8G8B8"); break;
		case D3DFMT_R5G6B5: PrintLog(ms_LogFile, L"D3DFMT_R5G6B5"); break;
		case D3DFMT_X1R5G5B5: PrintLog(ms_LogFile, L"D3DFMT_X1R5G5B5"); break;
		case D3DFMT_A1R5G5B5: PrintLog(ms_LogFile, L"D3DFMT_A1R5G5B5"); break;
		case D3DFMT_A4R4G4B4: PrintLog(ms_LogFile, L"D3DFMT_A4R4G4B4"); break;
		case D3DFMT_R3G3B2: PrintLog(ms_LogFile, L"D3DFMT_R3G3B2"); break;
		case D3DFMT_A8: PrintLog(ms_LogFile, L"D3DFMT_A8"); break;
		case D3DFMT_A8R3G3B2: PrintLog(ms_LogFile, L"D3DFMT_A8R3G3B2"); break;
		case D3DFMT_X4R4G4B4: PrintLog(ms_LogFile, L"D3DFMT_X4R4G4B4"); break;
		case D3DFMT_A2B10G10R10: PrintLog(ms_LogFile, L"D3DFMT_A2B10G10R10"); break;
		case D3DFMT_A8B8G8R8: PrintLog(ms_LogFile, L"D3DFMT_A8B8G8R8"); break;
		case D3DFMT_X8B8G8R8: PrintLog(ms_LogFile, L"D3DFMT_X8B8G8R8"); break;
		case D3DFMT_G16R16: PrintLog(ms_LogFile, L"D3DFMT_G16R16"); break;
		case D3DFMT_A2R10G10B10: PrintLog(ms_LogFile, L"D3DFMT_A2R10G10B10"); break;
		case D3DFMT_A16B16G16R16: PrintLog(ms_LogFile, L"D3DFMT_A16B16G16R16"); break;
		case D3DFMT_A8P8: PrintLog(ms_LogFile, L"D3DFMT_A8P8"); break;
		case D3DFMT_P8: PrintLog(ms_LogFile, L"D3DFMT_P8"); break;
		case D3DFMT_L8: PrintLog(ms_LogFile, L"D3DFMT_L8"); break;
		case D3DFMT_L16: PrintLog(ms_LogFile, L"D3DFMT_L16"); break;
		case D3DFMT_A8L8: PrintLog(ms_LogFile, L"D3DFMT_A8L8"); break;
		case D3DFMT_A4L4: PrintLog(ms_LogFile, L"D3DFMT_A4L4"); break;
		case D3DFMT_V8U8: PrintLog(ms_LogFile, L"D3DFMT_V8U8"); break;
		case D3DFMT_Q8W8V8U8: PrintLog(ms_LogFile, L"D3DFMT_Q8W8V8U8"); break;
		case D3DFMT_V16U16: PrintLog(ms_LogFile, L"D3DFMT_V16U16"); break;
		case D3DFMT_Q16W16V16U16: PrintLog(ms_LogFile, L"D3DFMT_Q16W16V16U16"); break;
		case D3DFMT_L6V5U5: PrintLog(ms_LogFile, L"D3DFMT_L6V5U5"); break;
		case D3DFMT_X8L8V8U8: PrintLog(ms_LogFile, L"D3DFMT_X8L8V8U8"); break;
		case D3DFMT_A2W10V10U10: PrintLog(ms_LogFile, L"D3DFMT_A2W10V10U10"); break;
		case D3DFMT_R16F: PrintLog(ms_LogFile, L"D3DFMT_R16F"); break;
		case D3DFMT_G16R16F: PrintLog(ms_LogFile, L"D3DFMT_G16R16F"); break;
		case D3DFMT_A16B16G16R16F: PrintLog(ms_LogFile, L"D3DFMT_A16B16G16R16F"); break;
		case D3DFMT_R32F: PrintLog(ms_LogFile, L"D3DFMT_R32F"); break;
		case D3DFMT_G32R32F: PrintLog(ms_LogFile, L"D3DFMT_G32R32F"); break;
		case D3DFMT_A32B32G32R32F: PrintLog(ms_LogFile, L"D3DFMT_A32B32G32R32F"); break;
		default: PrintLog(ms_LogFile, L"Unknown: " << ae_Format); break;
	}

	PrintLog(ms_LogFile, L" " << ai_Width << "x" << ai_Height << "\n");

	LPDIRECT3DTEXTURE9 lr_Texture_ = NULL;
	if(D3D_OK!=mr_Device_->CreateTexture(ai_Width, ai_Height, 1, D3DUSAGE_RENDERTARGET, ae_Format, D3DPOOL_DEFAULT,	&lr_Texture_,NULL))
	{
		PrintLog(ms_LogFile, L"SSAO: Unable to create texture.\n");
		return NULL;
	}

	return lr_Texture_;
}


IShader* k_Plugin::LoadShader(const std::string& as_FileName, const std::string& as_Path)
{
	USES_CONVERSION;

	IEnvironment& lr_Environment = object_cast<IEnvironment>(mr_ServiceLocator);

	std::string ls_WalhallPath = W2A(lr_Environment.get_WalhallPath().c_str());
	std::string ls_File = ls_WalhallPath+as_Path+"\\"+as_FileName;
	IShader* lr_Shader_ = mr_ShaderSystem_->LoadShader(as_FileName, ls_File);
	if (!lr_Shader_)
	{
		PrintLog(ms_LogFile, L"SSAO: ");
		PrintLog(ms_LogFile, A2W(ls_File.c_str()));
		PrintLog(ms_LogFile, L" shader not loaded. Critical error!\n");
	}
	else if (lr_Shader_ && !lr_Shader_->isValid())
	{
		PrintLog(ms_LogFile, L"SSAO: ");
		PrintLog(ms_LogFile, A2W(ls_File.c_str()));
		PrintLog(ms_LogFile, L" shader not loaded:\n");
		PrintLog(ms_LogFile, A2W(lr_Shader_->get_ShaderErrors().c_str()));
		PrintLog(ms_LogFile, L"\n");
	}
	else
	{
		PrintLog(ms_LogFile, L"SSAO: ");
		PrintLog(ms_LogFile, A2W(ls_File.c_str()));
		PrintLog(ms_LogFile, L" shader loaded.\n");
	}

	return lr_Shader_;
}


r_Texture k_Plugin::LoadTexture(const std::wstring& as_FileName, const std::wstring& as_Path)
{
	IEnvironment& lr_Environment = object_cast<IEnvironment>(mr_ServiceLocator);

	r_Texture lr_Texture;
	D3DXIMAGE_INFO lr_Info;

	std::wstring ls_WalhallPath = lr_Environment.get_WalhallPath();
	std::wstring ls_File = ls_WalhallPath+as_Path+L"\\"+as_FileName;
	if (D3D_OK != D3DXGetImageInfoFromFile(ls_File.c_str(), &lr_Info))
	{
		PrintLog(ms_LogFile, L"SSAO: ");
		PrintLog(ms_LogFile, ls_File.c_str());
		PrintLog(ms_LogFile, L" texture not loaded. Critical error!\n");
		return lr_Texture;
	}
	if (D3D_OK != D3DXCreateTextureFromFile(mr_Device_, ls_File.c_str(), &lr_Texture.mr_Texture_))
	{
		PrintLog(ms_LogFile, L"SSAO: ");
		PrintLog(ms_LogFile, ls_File.c_str());
		PrintLog(ms_LogFile, L" texture not loaded. Critical error!\n");
		return lr_Texture;
	}

	lr_Texture.me_Format = lr_Info.Format;
	lr_Texture.mi_Width = lr_Info.Width;
	lr_Texture.mi_Height = lr_Info.Height;

	return lr_Texture;
}


void k_Plugin::CopyRenderTarget(IDirect3DTexture9* ar_Texture_, RECT* ar_Rect_)
{
	if (!ar_Texture_)
	{
		PrintLog(ms_LogFile, L"SSAO: Destination texture is NULL.\n");
		return;
	}
	
	// get surfaces
	IDirect3DSurface9* lr_Dest_ = NULL;
	ar_Texture_->GetSurfaceLevel(0, &lr_Dest_);
	if (!lr_Dest_)
	{
		PrintLog(ms_LogFile, L"SSAO: Unable to get surface from destination texture.\n");
	}

	IDirect3DSurface9* lr_Source_ = NULL;
	mr_Device_->GetRenderTarget(0, &lr_Source_);	
	if (!lr_Source_)
	{
		PrintLog(ms_LogFile, L"SSAO: Unable to get surface from backbuffer.\n");
	}

	HRESULT lh_Result = mr_Device_->StretchRect(lr_Source_, ar_Rect_, lr_Dest_, ar_Rect_, D3DTEXF_NONE);
	if (FAILED(lh_Result))
	{
		PrintLog(ms_LogFile, L"SSAO: Failed to update our textures: " << lh_Result << "\n");
	}

	lr_Dest_->Release();
	lr_Source_->Release();
}


void k_Plugin::Render(IRenderable* ar_ScreenMask_, IShader* ar_Shader_)
{
	// update and prepare screenmask
	ar_ScreenMask_->BeginRender(mr_Device_);

	unsigned int Passes = ar_Shader_->get_PassCount();
	ar_Shader_->Begin();
	for(unsigned int CurrentPass = 0; CurrentPass<Passes; ++CurrentPass)
	{
		RenderPassReturnValue::Enumeration ReturnValue;
		do
		{
			ReturnValue = ar_Shader_->BeginPass(CurrentPass);
			if (ReturnValue != RenderPassReturnValue::RenderAndCallNot)
			{
				ar_ScreenMask_->Render(mr_Device_);
			}
		} while(ReturnValue == RenderPassReturnValue::RenderAndCallAgain);
		ar_Shader_->EndPass(); 
	}
	ar_Shader_->End(); 
}


void k_Plugin::RebuildTextures()
{
	LPDIRECT3DSURFACE9 lr_RendertargetSurface_;
	D3DSURFACE_DESC lr_RenderTargetDescription;

	mr_Device_->GetRenderTarget(0, &lr_RendertargetSurface_);
	lr_RendertargetSurface_->GetDesc(&lr_RenderTargetDescription);
	lr_RendertargetSurface_->Release();

	r_Texture lr_NewBackBufferTexture;
	lr_NewBackBufferTexture.me_Format = lr_RenderTargetDescription.Format;
	lr_NewBackBufferTexture.mi_Width = lr_RenderTargetDescription.Width;
	lr_NewBackBufferTexture.mi_Height = lr_RenderTargetDescription.Height;

	if (lr_NewBackBufferTexture != mr_BackBufferTexture)
	{
		lr_NewBackBufferTexture.mr_Texture_ = this->CreateTexture(lr_NewBackBufferTexture.mi_Width, lr_NewBackBufferTexture.mi_Height, lr_NewBackBufferTexture.me_Format);
		if (mr_BackBufferTexture.mr_Texture_)
			mr_BackBufferTexture.mr_Texture_->Release();
		mr_BackBufferTexture = lr_NewBackBufferTexture;

		r_Texture lr_NewZBackBufferTexture = lr_NewBackBufferTexture;
		lr_NewZBackBufferTexture.mr_Texture_ = this->CreateTexture(lr_NewZBackBufferTexture.mi_Width, lr_NewZBackBufferTexture.mi_Height, lr_NewZBackBufferTexture.me_Format);
		if (mr_ZBackBufferTexture.mr_Texture_)
			mr_ZBackBufferTexture.mr_Texture_->Release();
		mr_ZBackBufferTexture = lr_NewZBackBufferTexture;

		// rebuild screenmask
		SafeDelete (mr_ScreenMask_)
		mr_ScreenMask_ = new ScreenMask();
		mr_ScreenMask_->Create(mr_Device_, 0, 0, 1, 1);
		mr_ScreenMask_->SetTextureCoords(0,0,static_cast<float>(lr_NewBackBufferTexture.mi_Width)/lr_NewBackBufferTexture.mi_Width,static_cast<float>(lr_NewBackBufferTexture.mi_Height)/lr_NewBackBufferTexture.mi_Height);

		// rebuild Zscreenmask
		for(unsigned int i = 0; i < mk_ZScreenMaskList.size(); ++i)
		{
			SafeDelete (mk_ZScreenMaskList[i]);
		}
		mk_ZScreenMaskList.clear();
		SafeDelete(mk_ZScreenMaskBatch_);
		mk_ZScreenMaskBatch_ = new BatchScreenMask(mr_Device_, mi_ZScreenMaskCount*4);
		for(int i=0; i < mi_ZScreenMaskCount; ++i)
		{
			ScreenMask* lr_ScreenMask_ = NULL;
			lr_ScreenMask_ = new ScreenMask(true);
			lr_ScreenMask_->Create(mr_Device_, 0, 0, 1, 1);
			lr_ScreenMask_->SetTextureCoords(0,0,static_cast<float>(lr_NewBackBufferTexture.mi_Width)/lr_NewBackBufferTexture.mi_Width,static_cast<float>(lr_NewBackBufferTexture.mi_Height)/lr_NewBackBufferTexture.mi_Height);
			mk_ZScreenMaskList.push_back(lr_ScreenMask_);
		}

		SafeDelete(mr_ClearMask_);
		mr_ClearMask_ = new ScreenMask();
		mr_ClearMask_->Create(mr_Device_, 0, 0, 1, 1);
		mr_ClearMask_->SetTextureCoords(0,0,static_cast<float>(lr_NewBackBufferTexture.mi_Width)/lr_NewBackBufferTexture.mi_Width,static_cast<float>(lr_NewBackBufferTexture.mi_Height)/lr_NewBackBufferTexture.mi_Height);		
		
		// rebuild blur texture
		r_Texture lr_BlurBackBufferTexture;
		lr_BlurBackBufferTexture.me_Format = lr_RenderTargetDescription.Format;
		lr_BlurBackBufferTexture.mi_Width = lr_RenderTargetDescription.Width;
		lr_BlurBackBufferTexture.mi_Height = lr_RenderTargetDescription.Height;

		if (mr_AOBackBufferTexture.mr_Texture_)
			mr_AOBackBufferTexture.mr_Texture_->Release();
		mr_AOBackBufferTexture = lr_BlurBackBufferTexture;
		mr_AOBackBufferTexture.mr_Texture_ = this->CreateTexture(lr_BlurBackBufferTexture.mi_Width, lr_BlurBackBufferTexture.mi_Height, lr_BlurBackBufferTexture.me_Format);
	}
}


void k_Plugin::UpdateTextures()
{
	RECT lr_Rect;
	lr_Rect.left = lr_Rect.top = 0;
	lr_Rect.right = mr_BackBufferTexture.mi_Width;
	lr_Rect.bottom = mr_BackBufferTexture.mi_Height;

	this->CopyRenderTarget(mr_BackBufferTexture.mr_Texture_, &lr_Rect);

	if (this->IsViewportNew() || mb_NeedDepthUpdate)
	{
		for(unsigned int i=0; i<mk_ZScreenMaskList.size(); ++i)
		{
			float lf_Depth = mf_Depth - (i * (mf_DepthStep / mi_ZScreenMaskCount));
			//lf_Depth = std::pow(lf_Depth, 0.01f);

			float lf_RealDepth = static_cast<float>(this->CalcZDepth(lf_Depth));
			mk_ZScreenMaskList[i]->SetZ(lf_RealDepth); // real z depth

			lf_Depth = 1.0f - (i * (1.0f / mi_ZScreenMaskCount));
			mk_ZScreenMaskList[i]->SetTextureCoords(lf_Depth, lf_Depth, lf_Depth, lf_Depth, 1); // color
			mk_ZScreenMaskList[i]->ReCreate(mr_Device_);
			mk_ZScreenMaskBatch_->AddScreenMaskData(*mk_ZScreenMaskList[i], mr_Device_, i==0, i+1==mk_ZScreenMaskList.size());
		}

		mb_NeedDepthUpdate = false;
	}

	this->Render(mr_ClearMask_, mr_BlackRenderShader_);	

	this->Render(mk_ZScreenMaskBatch_, mr_DepthRenderShader_);
	
	this->CopyRenderTarget(mr_ZBackBufferTexture.mr_Texture_, &lr_Rect);
}


void k_Plugin::UpdateRendering()
{
	//return; // ## REMOVE ME
	// calculate AO
	float ddx = 1.0f/mr_AOBackBufferTexture.mi_Width;
	float ddy = 1.0f/mr_AOBackBufferTexture.mi_Height;
	mr_SSAOShader_->get_ParameterByName("sddx")->set_Float4(D3DXVECTOR4(ddx,ddy,0,0));
	this->Render(mr_ScreenMask_, mr_SSAOShader_);

	// blur AO result
	//this->AOBlur(mi_AOBlurSteps);

	if (mb_ShowAOOnly)
		return;

	// combine with orignal scene
	//this->Render(mr_ScreenMask_, mr_ShowTextureShader_);

	//this->Render(mr_ClearMask_, mr_AOBlurShader_);

	//this->Render(mr_ClearMask_, mr_ShowZTextureShader_);	
}


bool k_Plugin::CanRender()
{
	IDirect3DSurface9* lr_Surface_ = NULL;
	mr_Device_->GetRenderTarget(0, &lr_Surface_);

	IDirect3DSwapChain9* lr_SwapChain_ = NULL;
	for (unsigned int i=0; i<mr_Caps.NumberOfAdaptersInGroup; ++i)
	{
		mr_Device_->GetSwapChain(i, &lr_SwapChain_);
		if (lr_SwapChain_)
		{
			D3DPRESENT_PARAMETERS lr_Param;
			lr_SwapChain_->GetPresentParameters(&lr_Param);
			for (unsigned int j=0; j<lr_Param.BackBufferCount; ++j)
			{
				IDirect3DSurface9* lr_ScSurface_ = NULL;
				lr_SwapChain_->GetBackBuffer(j, D3DBACKBUFFER_TYPE_MONO, &lr_ScSurface_);
				if (lr_ScSurface_)
				{
					if (lr_ScSurface_==lr_Surface_)
					{
						lr_Surface_->Release();
						lr_ScSurface_->Release();
						lr_SwapChain_->Release();
						return true;
					}
					lr_ScSurface_->Release();
				}

			}
			
			lr_SwapChain_->Release();
		}
	}
	
	lr_Surface_->Release();
	return false;
}


double k_Plugin::CalcZDepth(double ad_PercentageDepth)
{

	D3DXVECTOR3 lr_Final;
	D3DXVECTOR3 lr_Depth(0, 0, static_cast<float>(1 + ad_PercentageDepth));

	D3DXMATRIX  lr_World;
	::D3DXMatrixIdentity(&lr_World);
	::D3DXVec3Project(
		&lr_Final,
		&lr_Depth,
		&mr_Viewport,
		&mr_ProjMatrix,
		&mr_ViewMatrix,
		&lr_World
		);

	return lr_Final.z;
}


bool k_Plugin::IsViewportNew()
{
	D3DVIEWPORT9 lr_Viewport;
	mr_Device_->GetViewport(&lr_Viewport);
	if (mr_Viewport.MaxZ == lr_Viewport.MaxZ && mr_Viewport.MinZ == lr_Viewport.MinZ)
		return false;

	mr_Viewport = lr_Viewport;

	return true;
}


void k_Plugin::AOBlur(int ai_Steps)
{
	RECT lr_Rect;
	lr_Rect.left = lr_Rect.top = 0;
	lr_Rect.right = static_cast<LONG>(mr_AOBackBufferTexture.mi_Width);
	lr_Rect.bottom = static_cast<LONG>(mr_AOBackBufferTexture.mi_Height);

	// first size down, yay	
	//this->Render(mr_ScreenMask_, mr_ShowZTextureShader_);	
	this->CopyRenderTarget(mr_AOBackBufferTexture.mr_Texture_, &lr_Rect);

	float ddx = 1.0f/mr_AOBackBufferTexture.mi_Width;
	float ddy = 1.0f/mr_AOBackBufferTexture.mi_Height;
	mr_AOBlurShader_->get_ParameterByName("sddx")->set_Float4(D3DXVECTOR4(ddx, ddy, 0, 0));
	for (int i=0; i<ai_Steps; ++i)
	{
		this->Render(mr_ScreenMask_, mr_AOBlurShader_);
		this->CopyRenderTarget(mr_AOBackBufferTexture.mr_Texture_, &lr_Rect);
	}
}
